package org.jdbcparser.driver;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.ddlutils.DatabaseOperationException;
import org.apache.ddlutils.PlatformUtils;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;

class DriverUtils {

	private static final PlatformUtils PLATFORM_UTILS = new PlatformUtils();
	private static final  ListMultimap<String, String> JDBC_PLATFORM_TO_DRIVERS = ArrayListMultimap.create();
	
	static {

		try {

			Field jdbcDriverToPlatformField = PlatformUtils.class.getField("jdbcDriverToPlatform");
			jdbcDriverToPlatformField.setAccessible(true);
			HashMap<String, String> jdbcDriverToPlatform = (HashMap<String, String>) jdbcDriverToPlatformField.get(PLATFORM_UTILS);
			Set<Entry<String, String>> driverPlatforms = jdbcDriverToPlatform.entrySet();

			for (Entry<String, String> driverPlatform : driverPlatforms) {
				JDBC_PLATFORM_TO_DRIVERS.put(driverPlatform.getValue(), driverPlatform.getKey());
			}

		} catch (Exception e) {
			throw new RuntimeException("Unable to load drivers information", e);
		}

	}

	public static String determineDatabaseType(DataSource dataSource) throws DatabaseOperationException {
		return PLATFORM_UTILS.determineDatabaseType(dataSource);
	}

	public static String determineDatabaseType(DataSource dataSource, String username, String password)
			throws DatabaseOperationException {
		return PLATFORM_UTILS.determineDatabaseType(dataSource, username, password);
	}

	public static String determineDatabaseType(String driverName, String jdbcConnectionUrl) {
		return PLATFORM_UTILS.determineDatabaseType(driverName, jdbcConnectionUrl);
	}
	
	public static String determineDatabaseType(String jdbcConnectionUrl) {
		return PLATFORM_UTILS.determineDatabaseType(null, jdbcConnectionUrl);
	}
	
	/**
	 * Returns a driver class name. The returned class name is the first driver 
	 * class loaded by the current classloader for the given connection url.
	 * @param jdbcConnectionUrl
	 * @return
	 */
	public static String determineDatabaseDriver(String jdbcConnectionUrl) {

		String databaseType = determineDatabaseType(jdbcConnectionUrl);
		List<String> drivers = JDBC_PLATFORM_TO_DRIVERS.get(databaseType);
		
		for (String driver : drivers) {
			try {
				DriverUtils.class.getClassLoader().loadClass(driver);
				return driver;
			} catch (ClassNotFoundException e) {
			}
		}

		return null;
	}
	
	
	public static Driver createDatabaseDriver(String driver) {
		try {
			return (Driver) DriverUtils.class.getClassLoader().loadClass(driver).newInstance();
		} catch (Exception e) {
			throw new IllegalArgumentException("Driver class not found in classpath: '" + driver + "'");
		}
	}
	
	public static String getTargetDriverURL(String jdbcConnectionUrl) {
		
		String targetURL = null;
		int parserParamIndex = jdbcConnectionUrl.indexOf("&jdbcparser");

		if(parserParamIndex != -1) {
			targetURL = jdbcConnectionUrl.substring(0, parserParamIndex);
		} else {
			targetURL = jdbcConnectionUrl;
		}
		
		return targetURL;
	}
	
	public static Map<String, String> getParserDriverParams(String jdbcConnectionUrl) {
		
		Map<String, String> params = new HashMap<String, String>();
		int parserParamIndex = jdbcConnectionUrl.indexOf("&jdbcparser");
		String paramsStr = null;
		
		if(parserParamIndex == -1) {
			throw new IllegalArgumentException("Invalid jdbcparser url: missing parameters");
		}
		
		paramsStr = jdbcConnectionUrl.substring(parserParamIndex);
		
		String[] paramsStrTokens = paramsStr.split("&");
		
		for (String token : paramsStrTokens) {
			String[] tokenParts = token.split("=");
			try {
				params.put(tokenParts[0], tokenParts[1]);
			} catch (Exception e) {
				throw new IllegalArgumentException("Invalid jdbcparser url parameter: '" + token + "'", e);
			}
		}
		
		return params;
	}

}
